# -*- coding: utf-8 -*-
"""
@Time ： 2021/5/19 14:00
@Auth ： Mr. royalday 2487625775
@Company ：XXX
@Function ：项目的文件管理视图
"""
import json

from django.http import JsonResponse, FileResponse, HttpResponse, StreamingHttpResponse
from django.shortcuts import render
from django.db import transaction
from django.forms import model_to_dict
from django.urls import reverse
from django.views.decorators.csrf import csrf_exempt
from django.utils.encoding import escape_uri_path

from web import models
from utils.tencent import cos
from scripts import download_cos_file
from web.forms.file import FolderModelForm, FileModelForm


def file(request, project_id):
    """
    文件列表
    :param request:
    :param project_id:
    :return:
    """
    parent_object = None  # 初始化当前路径父目录对象为空
    folder_id = request.GET.get('folder', "")  # 获取当前目录id,如果是在根目录则请求中没有folder_id字段
    # 判断请求中是否带有folder_id,并且folder_id是否是十进制数,如果带有说明当前路径不是根目录
    if folder_id.isdecimal():
        # 获取当前路径的目录对象
        parent_object = models.FileRepository.objects.filter(
            id=folder_id, file_type=2, project_id=project_id).first()

    # 如果是get请求,则直接返回控件
    if request.method == 'GET':
        breadcrumb_list = []
        parent = parent_object
        while parent:
            # breadcrumb_list.insert(0, {'id': parent.id, 'name': parent.file_name})
            breadcrumb_list.insert(0, model_to_dict(parent, ['id', 'file_name']))
            parent = parent.parent

        query_set = models.FileRepository.objects.filter(project_id=project_id, parent=parent_object)
        if parent_object:
            file_object_list = query_set.filter(parent=parent_object).order_by('-file_type')
        else:
            file_object_list = query_set.filter(project_id=project_id, parent__isnull=True).order_by('-file_type')
        form = FolderModelForm(request, parent_object)
        context = {
            'form': form,
            'file_object_list': file_object_list,
            'breadcrumb_list': breadcrumb_list,
            'folder_object': parent_object
        }

        return render(request, 'manage/file.html', context)

    # 修改文件夹-获取文件夹id
    fid = request.POST.get('fid')
    edit_object = None
    # 修改文件夹-如果fid格式正确，就从数据库获取对象
    if fid.isdecimal():
        edit_object = models.FileRepository.objects.filter(id=int(fid), file_type=2, project_id=project_id).first()
    # 修改文件夹-如果对象存在，就走修改代码；如果不存在就走新增代码
    if edit_object:
        # 如果是post请求,则获取form表单中的数据
        form = FolderModelForm(request, parent_object, data=request.POST, instance=edit_object)
    else:
        form = FolderModelForm(request, parent_object, data=request.POST)
    # 判断表单数据是否通过校验
    if form.is_valid():
        # 设置目录的所属项目为当前项目
        form.instance.project = request.tracer.project
        # 设置目录的文件类型为2:文件夹
        form.instance.file_type = 2
        # 设置目录的创建者为当前用户
        form.instance.create_user = request.tracer.user
        # 设置目录的更新者初始为当前用户
        form.instance.update_user = request.tracer.user
        # 设置目录的父目录为parent_object
        form.instance.parent = parent_object
        form.save()
        return JsonResponse({'status': True})

    # 如果以上所有判断都不通过则返回False及错误信息
    return JsonResponse({'status': False, 'error': form.errors})


def file_delete(request, project_id):
    fid = request.GET.get("fid", 0)
    if fid.isdecimal():
        file_object = models.FileRepository.objects.filter(id=fid, project_id=project_id).first()
        if file_object and file_object.file_type == 1:
            # 删除文件记录，cos中的文件，将容量返还给项目空间
            # 删除cos中的文件
            file_key = file_object.key_name
            cos.delete_file(bucket=request.tracer.project.bucket, key=file_key, region=request.tracer.project.region)
            # 数据库删除记录
            file_object.delete()
            # 返还项目容量
            project = models.Project.objects.filter(id=project_id).first()
            project.use_space -= file_object.file_size
            project.save()
            return JsonResponse({'status': True})
        else:
            # 删除文件夹，找到所有的子文件及文件夹，删除记录、删除cos中的文件并统计总容量，然后将容量返还给项目空间.
            total_size = 0  # 所有文件总大小
            folder_list = [file_object, ]  # 子文件夹列表
            return JsonResponse({'status': True})

    return JsonResponse({'status': False})


@csrf_exempt  # 免除csrf认证
def cos_credential(request, project_id):
    """
    获取cos对象存储的桶的临时凭证
    :param project_id:
    :param request:
    :return:
    """
    # 先做个容量限制，，当上传文件大小符合要求则返回临时凭证
    check_file_list = json.loads(request.body.decode("utf-8"))
    total_size = 0  # 所有上传文件的总大小
    for item in check_file_list:
        # 文件大小item['size']
        # 判断单文件大小是否超出限制
        max_size = request.tracer.price_policy.per_file_size  # 单文件最大上传大小
        if item['size'] > max_size * 1024 * 1024:
            return JsonResponse({"status": False,
                                 "error": f"单文件最大可上传{max_size}MB, 建议升级套餐！！！\n "
                                          f"文件【{item['name']}】超出限制"})
        total_size += item['size']

    # 项目最大可用空间
    project_space = request.tracer.price_policy.project_space
    # 当前项目已使用空间
    project_used_space = request.tracer.project.use_space
    # 项目总容量是否会达到上限
    if project_used_space + total_size > (project_space * 1024 * 1024 * 1024):
        return JsonResponse({"status": False,
                             "error": f"项目最大容量（{project_space}GB）达到上限， 请升级套餐！！！"})

    data = cos.credential(bucket=request.tracer.project.bucket, region=request.tracer.project.region)
    return JsonResponse({"status": True, "data": data})


@transaction.atomic
@csrf_exempt
def save_database(request, project_id):
    """
    将上传成功的文件写入到数据库中
    :param request:
    :param project_id:
    :return: Json字符串，成功/失败
    """
    form = FileModelForm(request, data=request.POST)
    if form.is_valid():
        # 写入文件记录到数据库
        # form.instance.file_type = 1
        # form.instance.create_user = request.tracer.user
        # form.instance.update_user = request.tracer.user
        # 使用上面的方法，再form.save()时，返回的对象无法通过get_xx_display()获取choice的中文名称，所以不推荐使用
        # 推荐使用下面的方法，手动创建记录
        # 创建字典，存入文件记录的各个键值对
        data_dict = form.cleaned_data
        data_dict.update({
            'file_type': 1,
            'create_user': request.tracer.user,
            'update_user': request.tracer.user,
            'project': request.tracer.project,
        })
        # 移除etag, 为安全考虑
        data_dict.pop('ETag')
        instance = models.FileRepository.objects.create(**data_dict)
        result = {
            'file_id': instance.id,
            "file_name": instance.file_name,
            "file_type": instance.get_file_type_display(),
            'file_size': instance.file_size,
            'create_user': instance.create_user.username,
            'create_datetime': instance.create_datetime.strftime("%Y{Y}-%m{m}-%d{d} %H:%M").
                                   format(Y='年', m='月', d='日'),
            'update_user': instance.update_user.username,
            'update_datetime': instance.update_datetime.strftime("%Y{Y}-%m{m}-%d{d} %H:%M").
                               format(Y='年', m='月', d='日'),
            'download_url': reverse('web:download_file', kwargs={'project_id': project_id, 'file_id': instance.id})
        }
        # 当前项目已使用空间
        # 文件记录保存到数据库的同时，项目的已使用空间累加
        # 注意，此处不能使用request.tracer.project 因为调用save方法时值不会修改
        try:
            project = models.Project.objects.select_for_update().get(id=project_id)
        except models.Project.DoesNotExist:
            pass
        now_use_space = project.use_space
        print(f"{now_use_space}+{data_dict['file_size']}", end="")
        project.use_space += data_dict['file_size']
        project.save()
        print(f"={project.use_space}")
        return JsonResponse({'status': True, 'data': result})

    return JsonResponse({'status': False, 'data': form.errors})


def download_file(request, project_id, file_id):
    """
    下载文件
    :param request:
    :param project_id:
    :param file_id:
    :return: 下载的文件内容
    """
    file_obj = models.FileRepository.objects.filter(project_id=project_id, id=file_id).first()
    if file_obj:
        file_url = file_obj.file_path
        file_name = file_obj.file_name

        # 获取文件内容
        res = download_cos_file.get_cos_file(url=file_url)

        # 文件分块处理，适用于大文件
        data = res.iter_content()
        # 将文件内容添加到响应体中
        response = HttpResponse(data)

        # 为响应加上响应头
        # 标注这是一个文件，通过文件名的后缀来识别这个文件类型
        response['Content-Disposition'] = "attachment; filename={}".format(escape_uri_path(file_name))
        # 标注响应体的内容为二进制模式，这种设定可以让任意文件都能正确下载
        response['Content-Type'] = 'application/octet-stream'
        return response

    return HttpResponse("文件不存在")
